package com.atguigu.tingshu.common.aspect;

import cn.hutool.core.util.RandomUtil;
import com.atguigu.tingshu.common.cache.TsCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
@Slf4j
@Aspect
public class TsCacheAspect {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    @Around("@annotation(tsCache)")
    public Object doBasicProfiling(ProceedingJoinPoint pjp, TsCache tsCache) throws Throwable{
        try {
            //1 使用key从redis中获取数据
            //1.1.1 获取注解前缀
            String prefix = tsCache.profix();
            //1.1.2 获取方法参数如果存在多个参数采用_拼接
            String params = "none";
            Object[] args = pjp.getArgs();
            if(args!=null&&args.length>0){
                List<Object> objects = Arrays.asList(args);
                params = objects.stream().map(Object::toString).collect(Collectors.joining("_"));
            }
            String dataKey = prefix+params;
            Object objectResult = redisTemplate.opsForValue().get(dataKey);
            //2 判断数据是否存在，存在则直接返回数据
            if (objectResult!=null){
                return objectResult;
            }else {
                //3 不存在则创建分布式锁，从数据库中获取数据，并保存到redis中
                String lockKey = dataKey + RedisConstant.CACHE_LOCK_SUFFIX;
                RLock lock = redissonClient.getLock(lockKey);
                boolean flag = lock.tryLock();
                if (flag){
                    try {
                        objectResult = pjp.proceed();
                        int ttl = RandomUtil.randomInt(60, 600);
                        redisTemplate.opsForValue().set(dataKey,objectResult,RedisConstant.ALBUM_TIMEOUT+ttl, TimeUnit.SECONDS);
                        return objectResult;
                    } finally {
                        //4 释放分布式锁
                        lock.unlock();
                    }
                }else{
                    Thread.sleep(1000);
                    return this.doBasicProfiling(pjp, tsCache);
                }
            }
        } catch (Exception e) {
            //5 兜底策略，从数据库中获取数据
            log.error("[缓存切面]获取数据异常：{}", e);
            return pjp.proceed();
        }

    }
}
